#include "serverprotocol.h"

#include <sstream>
#include <boost/archive/text_oarchive.hpp>
#include <boost/archive/text_iarchive.hpp>
#include "common/serialization.h"
#include "common/status.h"

ServerProtocol::ServerProtocol(FakeTcp* socket) : socket(socket) {
	connect(socket, SIGNAL(messageReady()), this, SLOT(readMessage()));
}

ServerProtocol::~ServerProtocol() { }

bool ServerProtocol::hasReadyActions() {
	return !(actions.empty());
}

ServerProtocolAction* ServerProtocol::getAction() {
	ServerProtocolAction* action = actions.at(0);
	actions.removeFirst();

	return action;
}

void ServerProtocol::readMessage() {
	while(socket->hasReadyMessage()) {
		QString ip;
		quint16 port;
		QString message = socket->getMessage(ip, port);
		//Get message type
		int first = message[0].digitValue();
		message = message.mid(1);

		switch(first) {
			case 1: //Connection message
				parseConnection(message, ip, port);
				break;
			case 2: //Chat message
				parseChat(message, ip, port);
				break;
			case 3: //User message
				parseUser(message, ip, port);
				break;
			case 4: //Friend message
				parseFriend(message, ip, port);
				break;
		}
	}
}

void ServerProtocol::parseConnection(QString message, QString ip, quint16 port) {
	//Get message type
	int first = message[0].digitValue();
	message = message.mid(1);

	switch(first) {
		case 1: { //User trying to connect
			std::istringstream serialStream(message.toStdString());
			boost::archive::text_iarchive serialReader(serialStream);
			QString login;
			QString password;

			serialReader >> login;
			serialReader >> password;
			ServerProtocolAction* newAction = new ServerConnectionAction(CFConnect, login, password);
			newAction->setIp(ip);
			newAction->setPort(port);
			actions.push_back(newAction);
			emit(readyAction());
			break;
		}
		case 2: { //User warning disconnect
			ServerProtocolAction* newAction = new ServerConnectionAction(CFDisconnect);
			newAction->setIp(ip);
			newAction->setPort(port);
			actions.push_back(newAction);
			emit(readyAction());
			break;
		}
		case 4: { //Got keep alive message
			ServerProtocolAction* newAction = new ServerConnectionAction(CFAnswerKeepAlive);
			newAction->setIp(ip);
			newAction->setPort(port);
			actions.push_back(newAction);
			emit(readyAction());
			break;
		}
	}
}

void ServerProtocol::parseChat(QString message, QString ip, quint16 port) {
	//Get message type
	int first = message[0].digitValue();
	message = message.mid(1);
	std::istringstream serialStream(message.toStdString());
	boost::archive::text_iarchive serialReader(serialStream);

	switch(first) {
		case 1: { //User asking for new chat
			int id;
			serialReader >> id;

			ServerProtocolAction* newAction = new ServerChatAction(HFStart, id);
			newAction->setIp(ip);
			newAction->setPort(port);
			actions.push_back(newAction);
			emit(readyAction());
			break;
		}
		case 2: { //User inviting friend to chat
			Chat* chat;
			int id;
			serialReader >> chat;
			serialReader >> id;

			ServerProtocolAction* newAction = new ServerChatAction(HFInvite, chat, id);
			newAction->setIp(ip);
			newAction->setPort(port);
			actions.push_back(newAction);
			emit(readyAction());
			break;
		}
		case 3: { //User sending message to chat
			Chat* chat;
			QString message;
			serialReader >> chat;
			serialReader >> message;

			ServerProtocolAction* newAction = new ServerChatAction(HFSend, chat, message);
			newAction->setIp(ip);
			newAction->setPort(port);
			actions.push_back(newAction);
			emit(readyAction());
			break;
		}
		case 4: { //User left chat
			Chat* chat;
			serialReader >> chat;

			ServerProtocolAction* newAction = new ServerChatAction(HFExit, chat);
			newAction->setIp(ip);
			newAction->setPort(port);
			actions.push_back(newAction);
			emit(readyAction());
			break;
		}
	}
}

void ServerProtocol::parseUser(QString message, QString ip, quint16 port) {
	//Get message type
	int first = message[0].digitValue();
	message = message.mid(1);
	std::istringstream serialStream(message.toStdString());
	boost::archive::text_iarchive serialReader(serialStream);

	switch(first) {
		case 3: { //User asking for to change password.
			QString oldPassword;
			QString newPassword;
			serialReader >> oldPassword;
			serialReader >> newPassword;

			ServerProtocolAction* newAction = new ServerUserAction(UFPassword, oldPassword, newPassword);
			newAction->setIp(ip);
			newAction->setPort(port);
			actions.push_back(newAction);
			emit(readyAction());
			break;
		}
		case 4: { //User changing status
			Status newStatus;
			serialReader >> newStatus;

			ServerProtocolAction* newAction = new ServerUserAction(UFStatus, newStatus);
			newAction->setIp(ip);
			newAction->setPort(port);
			actions.push_back(newAction);
			emit(readyAction());
			break;
		}
		case 5: { //New user account
			QString login;
			QString password;

			serialReader >> login;
			serialReader >> password;
			ServerProtocolAction* newAction = new ServerUserAction(UFSignup, login, password);
			newAction->setIp(ip);
			newAction->setPort(port);
			actions.push_back(newAction);
			emit(readyAction());
		}
	}
}

void ServerProtocol::parseFriend(QString message, QString ip, quint16 port) {
	//Get message type
	int first = message[0].digitValue();
	message = message.mid(1);
	std::istringstream serialStream(message.toStdString());
	boost::archive::text_iarchive serialReader(serialStream);

	switch(first) {
		case 1: { //User inviting other person
			QString friendLogin;
			serialReader >> friendLogin;

			ServerProtocolAction* newAction = new ServerFriendAction(FFInvite, friendLogin);
			newAction->setIp(ip);
			newAction->setPort(port);
			actions.push_back(newAction);
			emit(readyAction());
			break;
		}
		case 2: { //User accepted invitation
			QString friendLogin;
			serialReader >> friendLogin;

			ServerProtocolAction* newAction = new ServerFriendAction(FFAccept, friendLogin);
			newAction->setIp(ip);
			newAction->setPort(port);
			actions.push_back(newAction);
			emit(readyAction());
			break;
		}
		case 3: { //User denied invitation
			QString friendLogin;
			serialReader >> friendLogin;

			ServerProtocolAction* newAction = new ServerFriendAction(FFDeny, friendLogin);
			newAction->setIp(ip);
			newAction->setPort(port);
			actions.push_back(newAction);
			emit(readyAction());
			break;
		}
		case 5: { //User removed friend
			Friend* f;
			serialReader >> f;
			
			ServerProtocolAction* newAction = new ServerFriendAction(FFRemove, f);
			newAction->setIp(ip);
			newAction->setPort(port);
			actions.push_back(newAction);
			emit(readyAction());
			break;
		}
	}
}

/*
 * Server Connection Protocol
 */

void ServerConnectionProtocol::sendKeepAlive(FakeTcp* socket) {
	socket->sendMessage("13");
}

void ServerConnectionProtocol::deny(FakeTcp* socket) {
	socket->sendMessage("15");
}

void ServerConnectionProtocol::accept(FakeTcp* socket, Friend* f) {
	std::ostringstream serialStream;
	boost::archive::text_oarchive archive(serialStream);
	archive & f;

	QString serialString(serialStream.str().c_str());
	socket->sendMessage("16" + serialString);
}

/*
 * Server Chat Protocol
 */

void ServerChatProtocol::start(FakeTcp* socket, Chat* chat) {
	std::ostringstream serialStream;
	boost::archive::text_oarchive archive(serialStream);
	archive & chat;

	QString serialString(serialStream.str().c_str());
	socket->sendMessage("21" + serialString);
}

void ServerChatProtocol::send(FakeTcp* socket, Chat* chat, Friend* f, QString message) {
	std::ostringstream serialStream;
	boost::archive::text_oarchive archive(serialStream);
	archive & chat;
	archive & f;
	archive & message;

	QString serialString(serialStream.str().c_str());
	socket->sendMessage("23" + serialString);
}

void ServerChatProtocol::exit(FakeTcp* socket, Chat* chat, Friend* f) {
	std::ostringstream serialStream;
	boost::archive::text_oarchive archive(serialStream);
	archive & chat;
	archive & f;

	QString serialString(serialStream.str().c_str());
	socket->sendMessage("24" + serialString);
}

void ServerChatProtocol::enter(FakeTcp* socket, Chat* chat, Friend* f) {
	std::ostringstream serialStream;
	boost::archive::text_oarchive archive(serialStream);
	archive & chat;
	archive & f;

	QString serialString(serialStream.str().c_str());
	socket->sendMessage("26" + serialString);
}

void ServerChatProtocol::terminate(FakeTcp* socket, Chat* chat) {
	std::ostringstream serialStream;
	boost::archive::text_oarchive archive(serialStream);
	archive & chat;

	QString serialString(serialStream.str().c_str());
	socket->sendMessage("25" + serialString);
}

/*
 * Server User Protocol
 */

void ServerUserProtocol::friendStatus(FakeTcp* socket, Friend* f) {
	std::ostringstream serialStream;
	boost::archive::text_oarchive archive(serialStream);
	archive & f;

	QString serialString(serialStream.str().c_str());
	socket->sendMessage("31" + serialString);
}

void ServerUserProtocol::friendList(FakeTcp* socket, QList<Friend*> friends) {
	std::ostringstream serialStream;
	boost::archive::text_oarchive archive(serialStream);
	archive & friends;

	QString serialString(serialStream.str().c_str());
	socket->sendMessage("32" + serialString);
}

void ServerUserProtocol::changePassword(FakeTcp* socket, bool isPasswordOk) {
	std::ostringstream serialStream;
	boost::archive::text_oarchive archive(serialStream);
	archive & isPasswordOk;

	QString serialString(serialStream.str().c_str());
	socket->sendMessage("33" + serialString);
}

/*
 * Server Friend Protocol
 */

void ServerFriendProtocol::invite(FakeTcp* socket, QString login) {
	std::ostringstream serialStream;
	boost::archive::text_oarchive archive(serialStream);
	archive & login;

	QString serialString(serialStream.str().c_str());
	socket->sendMessage("41" + serialString);
}

void ServerFriendProtocol::deny(FakeTcp* socket, QString login) {
	std::ostringstream serialStream;
	boost::archive::text_oarchive archive(serialStream);
	archive & login;

	QString serialString(serialStream.str().c_str());
	socket->sendMessage("43" + serialString);
}

void ServerFriendProtocol::add(FakeTcp* socket, Friend* f) {
	std::ostringstream serialStream;
	boost::archive::text_oarchive archive(serialStream);
	archive & f;

	QString serialString(serialStream.str().c_str());
	socket->sendMessage("44" + serialString);
}

void ServerFriendProtocol::remove(FakeTcp* socket, Friend* f) {
	std::ostringstream serialStream;
	boost::archive::text_oarchive archive(serialStream);
	archive & f;

	QString serialString(serialStream.str().c_str());
	socket->sendMessage("45" + serialString);
}
